﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Effects.Application
{
    /// <summary>
    ///  Form displaying progress of some process.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   Class contains <see cref="System.Windows.Forms.ProgressBar"/> to display current progress and <see cref="System.Windows.Forms.Button"/> for canceling the process.
    ///   Current progress is set by <see cref="ProgressForm.Progress"/> property. Once it has been set operation is over and class doesnt' expect any further progress changes.
    ///  </para>
    /// </remarks>
    public partial class ProgressForm : Form
    {
        /// <summary>
        ///  Creates new ProgressForm.
        /// </summary>
        public ProgressForm()
        {
            InitializeComponent();

            Text = "Operation's progress";
            // settings minimum and maximum values to form's progress bar
            DisplayProgressBar.Minimum = 0;
            DisplayProgressBar.Maximum = 20;
            DisplayProgressBar.Value = 0;
            DisplayProgressBar.ForeColor = System.Drawing.Color.Red;
            DisplayProgressBar.Style = ProgressBarStyle.Blocks;

            finished = false;

            CancelButton.Click += new EventHandler(this.CancelButton_clicked);
            this.FormClosing += new FormClosingEventHandler(this.Closing);

            InitializeInvokers();

            // disable X-button
            this.ControlBox = false;
        }

        /// <summary>
        ///  Creates new ProgressForm with given title.
        /// </summary>
        /// <param name="title"> Title of the form.  </param>
        public ProgressForm(string title):this()
        {
            this.Text = title;
        }

        /// <summary>
        ///  Initializes invokers for multithread operations.
        /// </summary>
        private void InitializeInvokers()
        {
            SetProgressBarValueInvoker = new SetProgressBarValueInvokerDelegate(this.SetDispayProgressBarValue);
            SetLabelTextInvoker = new SetControlTextInvokerDelegate(this.SetProgressLabelText);
            SetCancelButtonTextInvoker = new SetControlTextInvokerDelegate(this.SetCancelButtonText);
            SetCancelButtonEnabledInvoker = new SetButtonEnabledInvokerDelegate(this.SetCancelButtonEnabled);
        }

        /// <summary>
        ///  True when progress is set to 100%.
        /// </summary>
        bool finished;

        /// <summary>
        ///  Gets or sets the progress displayed on the form's progress bar. Acceptable are values from [0;100] (It's like percentage.)
        /// </summary>
        public int Progress
        {
            get {return DisplayProgressBar.Value;}
            set
            {
                if (value >= 0 && value <= 100)
                {
                    string cancelButtonText;

                    if (value == 100)
                    {
                        finished = true;
                        cancelButtonText = "Close";

                    }
                    else
                    {
                        finished = false;
                        cancelButtonText = "Cancel";
                    }

                    if (InvokeRequired)
                    {
                        BeginInvoke(SetProgressBarValueInvoker, new object[] { value / 5 });
                        BeginInvoke(SetLabelTextInvoker, new object[] { value + "% completed" });
                        BeginInvoke(SetCancelButtonTextInvoker, new object[] { cancelButtonText });
                        if(finished)
                            BeginInvoke(SetCancelButtonEnabledInvoker, new object[] { true });
                    }
                    else
                    {
                        DisplayProgressBar.Value = value / 5;
                        ProgressLabel.Text = value + "% completed";
                        CancelButton.Text = cancelButtonText;
                        if(finished)
                            CancelButton.Enabled = true;
                    }
                }
            }
        }

        /// <summary>
        ///  Inidicates whether cancelling of the operation is enabled.
        /// </summary>
        private bool cancelEnabled;


        /// <summary>
        ///  Enables or disables canceling of the operation.
        /// </summary>
        public bool CancelEnabled
        {
            get { return cancelEnabled; }
            set
            {
                cancelEnabled = value;
                if (InvokeRequired)
                    BeginInvoke(SetCancelButtonEnabledInvoker, new object[] { value });
                else
                    CancelButton.Enabled = value;
            }
        }

        #region multithread_set_progress
        // setting progress from all threads

        #region progress_label

        /// <summary>
        ///  Delegate for setting text to the control.
        /// </summary>
        /// <param name="text"> Text to set to the control. </param>
        private delegate void SetControlTextInvokerDelegate(string text);

        /// <summary>
        ///  Invokes SetProgressLabelText.
        /// </summary>
        private SetControlTextInvokerDelegate SetLabelTextInvoker;

        /// <summary>
        ///  Displays given text by setting it to the label.
        /// </summary>
        /// <param name="text"> Text to set to the control </param>
        private void SetProgressLabelText(string text)
        {
            ProgressLabel.Text = text;
        }

        #endregion

        #region progress_bar

        /// <summary>
        ///  Delegate for setting value of the progress bar.
        /// </summary>
        /// <param name="value"> Value from interval [0;100] - percentage of progress </param>
        private delegate void SetProgressBarValueInvokerDelegate(int value);

        /// <summary>
        ///  Invokes SetDisplayProgressBarValue.
        /// </summary>
        private SetProgressBarValueInvokerDelegate SetProgressBarValueInvoker;

        /// <summary>
        ///  Sets given value to progress bar.
        /// </summary>
        /// <param name="value"> Value from interval [0;100] </param>
        private void SetDispayProgressBarValue(int value)
        {
            DisplayProgressBar.Value = value;
        }

        #endregion

        #region cancel_button_text
        /// <summary>
        ///  Invokes SetCancelButtonText method
        /// </summary>
        private SetControlTextInvokerDelegate SetCancelButtonTextInvoker;

        /// <summary>
        ///  Sets text to Caoncel button on the form.
        /// </summary>
        /// <param name="text"> Text to set to the button </param>
        private void SetCancelButtonText(string text)
        {
            CancelButton.Text = text;
        }

        #endregion

        #region cancel_button_enabled
        /// <summary>
        ///  Delegate for enabling Cancel button.
        /// </summary>
        /// <param name="enabled"> Enabled parameter: true - enabled, false - disabled. </param>
        private delegate void SetButtonEnabledInvokerDelegate(bool enabled);

        /// <summary>
        ///  Ivokes SetCancelButtonEnabled method.
        /// </summary>
        private SetButtonEnabledInvokerDelegate SetCancelButtonEnabledInvoker;

        /// <summary>
        ///  Enables Cancel button.
        /// </summary>
        /// <param name="enabled"> Enabled parameter: true - enabled, false - disabled. </param>
        private void SetCancelButtonEnabled(bool enabled)
        {
            CancelButton.Enabled = enabled;
        }
        #endregion

        #endregion

        /// <summary>
        ///  Occurs when users wants to cancel the process.
        /// </summary>
        public event EventHandler CancelProcess;

        /// <summary>
        ///  CancelButton event handler.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. == Cancel Button </param>
        /// <param name="args"> Event arguments. </param>
        private void CancelButton_clicked(object sender, EventArgs args)
        {
            CancelAttempt();
        }

        /// <summary>
        ///  Handling all attemps to cancel the process: CancelButton click or closing the form.
        /// </summary>
        private void CancelAttempt()
        {
            if (!finished)
            {
                // ask user "Do you really want to ...."
                DialogResult answer = MessageBox.Show("Do you want to cancel running process?", "Cancel operation?", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (answer == DialogResult.Yes)
                {
                    if (CancelProcess != null)
                        CancelProcess(this, new EventArgs());
                    finished = true;
                    this.Close();
                }
            }

            this.Close();
        }

        /// <summary>
        ///  Firstly asks user and then closes the from.
        /// </summary>
        /// <param name="sender"> Obejct that has raised the closing event. </param>
        /// <param name="args"> Evetn arguments. </param>
        private void Closing(object sender, FormClosingEventArgs args)
        {
            if (!finished)
            {
                args.Cancel = true;
                CancelAttempt();
            }
        }

        /// <summary>
        ///  Close the form immediately
        /// </summary>
        public void CloseWithoutAsking()
        {
            finished = true;
            if (InvokeRequired)
                BeginInvoke(new MethodInvoker(Close));
            else
                Close();
        }
    }
}
